package ch.bfh.ti.barcode.io;

import java.io.IOException;
import java.io.InputStream;

import ch.bfh.ti.barcode.multiple.MultipleCodeReader;

/**
 * A simple implementation of the abstract {@link InputStream} class reading from the
 * {@link MultipleCodeReader}. The implementation does not do any buffering. It just
 * reads a whole code and provides methods to read the code piece by piece.
 * 
 * @author Dominik von Burg
 * 
 */
public class CodeInputStream extends InputStream {
	private MultipleCodeReader reader;
	private byte[] buffer;
	private int bufferIndex = 0;

	/**
	 * Creates a new {@link InputStream} on top of the given {@link MultipleCodeReader}.
	 * 
	 * @param reader
	 *            An instance of the {@link MultipleCodeReader} which the data is read
	 *            from.
	 */
	public CodeInputStream(MultipleCodeReader reader) {
		this.reader = reader;
	}

	/**
	 * Reads byte by byte from the {@link MultipleCodeReader}. The whole block read from
	 * the {@link MultipleCodeReader} is kept as a buffer. As soon as the buffer is
	 * empty, a new block is being read.
	 */
	@Override
	public int read() throws IOException {
		if (buffer == null || buffer.length <= bufferIndex) {
			// All frames are received.
			if (!reader.hasMore())
				return -1;

			buffer = reader.read();
			
			bufferIndex = 0;
		}

		bufferIndex++;
		return buffer[bufferIndex - 1] & 0xff;
	}

	/**
	 * Reads as many bytes as possible from the {@link MultipleCodeReader}. Always keeps
	 * the whole block read from the {@link MultipleCodeReader} as a buffer. Returns how
	 * many bytes are actually being read from the {@link MultipleCodeReader}.
	 */
	@Override
	public int read(byte[] b) throws IOException {
		int read = 0;
		
		while (read < b.length) {
			if (buffer == null || buffer.length <= bufferIndex) {
				// All frames are received.
				if (!reader.hasMore())
					return read;
				
				buffer = reader.read();
				
				bufferIndex = 0;
			}

			int copyCount = Math.min(b.length - read, buffer.length
					- bufferIndex);
			System.arraycopy(buffer, bufferIndex, b, read, copyCount);
			read += copyCount;
			bufferIndex += copyCount;
		}

		return read;
	}

}
